/*
 * UAE - The Un*x Amiga Emulator
 *
 * Additional Win32 helper functions not calling any system routines
 *
 * (c) 1997 Mathias Ortmann
 * (c) 1999-2001 Brian King
 * (c) 2000-2001 Bernd Roesch
 * (c) 2002 Toni Wilen
 */

#include "sysconfig.h"
#include "keyboard.h"
#include "options.h"
#include "inputdevice.h"
#include "drawing.h"
#include "win32.h"

static struct uae_input_device_kbr_default keytrans[] =
{
    { DIK_ESCAPE, INPUTEVENT_KEY_ESC },

    { DIK_F1, INPUTEVENT_KEY_F1 },
    { DIK_F2, INPUTEVENT_KEY_F2 },
    { DIK_F3, INPUTEVENT_KEY_F3 },
    { DIK_F4, INPUTEVENT_KEY_F4 },
    { DIK_F5, INPUTEVENT_KEY_F5 },

    { DIK_F6, INPUTEVENT_KEY_F6 },
    { DIK_F7, INPUTEVENT_KEY_F7 },
    { DIK_F8, INPUTEVENT_KEY_F8 },
    { DIK_F9, INPUTEVENT_KEY_F9 },
    { DIK_F10, INPUTEVENT_KEY_F10 },

    { DIK_1, INPUTEVENT_KEY_1 },
    { DIK_2, INPUTEVENT_KEY_2 },
    { DIK_3, INPUTEVENT_KEY_3 },
    { DIK_4, INPUTEVENT_KEY_4 },
    { DIK_5, INPUTEVENT_KEY_5 },
    { DIK_6, INPUTEVENT_KEY_6 },
    { DIK_7, INPUTEVENT_KEY_7 },
    { DIK_8, INPUTEVENT_KEY_8 },
    { DIK_9, INPUTEVENT_KEY_9 },
    { DIK_0, INPUTEVENT_KEY_0 },

    { DIK_TAB, INPUTEVENT_KEY_TAB },

    { DIK_A, INPUTEVENT_KEY_A },
    { DIK_B, INPUTEVENT_KEY_B },
    { DIK_C, INPUTEVENT_KEY_C },
    { DIK_D, INPUTEVENT_KEY_D },
    { DIK_E, INPUTEVENT_KEY_E },
    { DIK_F, INPUTEVENT_KEY_F },
    { DIK_G, INPUTEVENT_KEY_G },
    { DIK_H, INPUTEVENT_KEY_H },
    { DIK_I, INPUTEVENT_KEY_I },
    { DIK_J, INPUTEVENT_KEY_J },
    { DIK_K, INPUTEVENT_KEY_K },
    { DIK_L, INPUTEVENT_KEY_L },
    { DIK_M, INPUTEVENT_KEY_M },
    { DIK_N, INPUTEVENT_KEY_N },
    { DIK_O, INPUTEVENT_KEY_O },
    { DIK_P, INPUTEVENT_KEY_P },
    { DIK_Q, INPUTEVENT_KEY_Q },
    { DIK_R, INPUTEVENT_KEY_R },
    { DIK_S, INPUTEVENT_KEY_S },
    { DIK_T, INPUTEVENT_KEY_T },
    { DIK_U, INPUTEVENT_KEY_U },
    { DIK_W, INPUTEVENT_KEY_W },
    { DIK_V, INPUTEVENT_KEY_V },
    { DIK_X, INPUTEVENT_KEY_X },
    { DIK_Y, INPUTEVENT_KEY_Y },
    { DIK_Z, INPUTEVENT_KEY_Z },

    { DIK_CAPITAL, INPUTEVENT_KEY_CAPS_LOCK, ID_FLAG_TOGGLE },

    { DIK_NUMPAD1, INPUTEVENT_KEY_NP_1 },
    { DIK_NUMPAD2, INPUTEVENT_KEY_NP_2 },
    { DIK_NUMPAD3, INPUTEVENT_KEY_NP_3 },
    { DIK_NUMPAD4, INPUTEVENT_KEY_NP_4 },
    { DIK_NUMPAD5, INPUTEVENT_KEY_NP_5 },
    { DIK_NUMPAD6, INPUTEVENT_KEY_NP_6 },
    { DIK_NUMPAD7, INPUTEVENT_KEY_NP_7 },
    { DIK_NUMPAD8, INPUTEVENT_KEY_NP_8 },
    { DIK_NUMPAD9, INPUTEVENT_KEY_NP_9 },
    { DIK_NUMPAD0, INPUTEVENT_KEY_NP_0 },
    { DIK_DECIMAL, INPUTEVENT_KEY_NP_PERIOD },
    { DIK_ADD, INPUTEVENT_KEY_NP_ADD },
    { DIK_SUBTRACT, INPUTEVENT_KEY_NP_SUB },
    { DIK_MULTIPLY, INPUTEVENT_KEY_NP_MUL },
    { DIK_DIVIDE, INPUTEVENT_KEY_NP_DIV },
    { DIK_NUMPADENTER, INPUTEVENT_KEY_ENTER },

    { DIK_MINUS, INPUTEVENT_KEY_SUB },
    { DIK_EQUALS, INPUTEVENT_KEY_EQUALS },
    { DIK_BACK, INPUTEVENT_KEY_BACKSPACE },
    { DIK_RETURN, INPUTEVENT_KEY_RETURN },
    { DIK_SPACE, INPUTEVENT_KEY_SPACE },

    { DIK_LSHIFT, INPUTEVENT_KEY_SHIFT_LEFT },
    { DIK_LCONTROL, INPUTEVENT_KEY_CTRL },
    { DIK_LWIN, INPUTEVENT_KEY_AMIGA_LEFT },
    { DIK_LMENU, INPUTEVENT_KEY_ALT_LEFT },
    { DIK_RMENU, INPUTEVENT_KEY_ALT_RIGHT },
    { DIK_RWIN, INPUTEVENT_KEY_AMIGA_RIGHT },
    { DIK_APPS, INPUTEVENT_KEY_AMIGA_RIGHT },
    { DIK_RCONTROL, INPUTEVENT_KEY_CTRL_RIGHT },
    { DIK_RSHIFT, INPUTEVENT_KEY_SHIFT_RIGHT },

    { DIK_UP, INPUTEVENT_KEY_CURSOR_UP },
    { DIK_DOWN, INPUTEVENT_KEY_CURSOR_DOWN },
    { DIK_LEFT, INPUTEVENT_KEY_CURSOR_LEFT },
    { DIK_RIGHT, INPUTEVENT_KEY_CURSOR_RIGHT },

    { DIK_INSERT, INPUTEVENT_KEY_AMIGA_LEFT },
    { DIK_DELETE, INPUTEVENT_KEY_DEL },
    { DIK_HOME, INPUTEVENT_KEY_AMIGA_RIGHT },
    { DIK_NEXT, INPUTEVENT_KEY_HELP },

    { DIK_LBRACKET, INPUTEVENT_KEY_LEFTBRACKET },
    { DIK_RBRACKET, INPUTEVENT_KEY_RIGHTBRACKET },
    { DIK_SEMICOLON, INPUTEVENT_KEY_SEMICOLON },
    { DIK_APOSTROPHE, INPUTEVENT_KEY_SINGLEQUOTE },
    { DIK_GRAVE, INPUTEVENT_KEY_BACKQUOTE },
    { DIK_BACKSLASH, INPUTEVENT_KEY_BACKSLASH },
    { DIK_COMMA, INPUTEVENT_KEY_COMMA },
    { DIK_PERIOD, INPUTEVENT_KEY_PERIOD },
    { DIK_SLASH, INPUTEVENT_KEY_DIV },
    { DIK_OEM_102, INPUTEVENT_KEY_30 },

    //  { DIK_VOLUMEDOWN, INPUTEVENT_SPC_MASTER_VOLUME_DOWN },
    //  { DIK_VOLUMEUP, INPUTEVENT_SPC_MASTER_VOLUME_UP },
    //  { DIK_MUTE, INPUTEVENT_SPC_MASTER_VOLUME_MUTE },

    { DIK_HOME, INPUTEVENT_KEY_70 },
    { DIK_END, INPUTEVENT_KEY_71 },
    //     { DIK_SYSRQ, INPUTEVENT_KEY_6E },
    //     { DIK_F12, INPUTEVENT_KEY_6F },
    { DIK_INSERT, INPUTEVENT_KEY_47 },
    //     { DIK_PRIOR, INPUTEVENT_KEY_48 },
    { DIK_PRIOR, INPUTEVENT_SPC_FREEZEBUTTON },
    { DIK_NEXT, INPUTEVENT_KEY_49 },
    { DIK_F11, INPUTEVENT_KEY_4B },

    { DIK_MEDIASTOP, INPUTEVENT_KEY_CDTV_STOP },
    { DIK_PLAYPAUSE, INPUTEVENT_KEY_CDTV_PLAYPAUSE },
    { DIK_PREVTRACK, INPUTEVENT_KEY_CDTV_PREV },
    { DIK_NEXTTRACK, INPUTEVENT_KEY_CDTV_NEXT },

    { -1, 0 }
};

static int kb_np[] = { DIK_NUMPAD4, -1, DIK_NUMPAD6, -1, DIK_NUMPAD8, -1, DIK_NUMPAD2, -1, DIK_NUMPAD0, DIK_NUMPAD5, -1, DIK_DECIMAL, -1, DIK_NUMPADENTER, -1, -1 };
static int kb_ck[] = { DIK_LEFT, -1, DIK_RIGHT, -1, DIK_UP, -1, DIK_DOWN, -1, DIK_RCONTROL, DIK_RMENU, -1, DIK_RSHIFT, -1, -1 };
static int kb_se[] = { DIK_A, -1, DIK_D, -1, DIK_W, -1, DIK_S, -1, DIK_LMENU, -1, DIK_LSHIFT, -1, -1 };
static int kb_np3[] = { DIK_NUMPAD4, -1, DIK_NUMPAD6, -1, DIK_NUMPAD8, -1, DIK_NUMPAD2, -1, DIK_NUMPAD0, DIK_NUMPAD5, -1, DIK_DECIMAL, -1, DIK_NUMPADENTER, -1, -1 };
static int kb_ck3[] = { DIK_LEFT, -1, DIK_RIGHT, -1, DIK_UP, -1, DIK_DOWN, -1, DIK_RCONTROL, -1, DIK_RSHIFT, -1, DIK_RMENU, -1, -1 };
static int kb_se3[] = { DIK_A, -1, DIK_D, -1, DIK_W, -1, DIK_S, -1, DIK_LMENU, -1, DIK_LSHIFT, -1, DIK_LCONTROL, -1, -1 };

static int kb_cd32_np[] = { DIK_NUMPAD4, -1, DIK_NUMPAD6, -1, DIK_NUMPAD8, -1, DIK_NUMPAD2, -1, DIK_NUMPAD0, DIK_NUMPAD5, DIK_NUMPAD1, -1, DIK_DECIMAL, DIK_NUMPAD3, -1, DIK_NUMPAD7, -1, DIK_NUMPAD9, -1, DIK_DIVIDE, -1, DIK_SUBTRACT, -1, DIK_MULTIPLY, -1, -1 };
static int kb_cd32_ck[] = { DIK_LEFT, -1, DIK_RIGHT, -1, DIK_UP, -1, DIK_DOWN, -1, DIK_RCONTROL, -1, DIK_RMENU, -1, DIK_NUMPAD7, -1, DIK_NUMPAD9, -1, DIK_DIVIDE, -1, DIK_SUBTRACT, -1, DIK_MULTIPLY, -1, -1 };
static int kb_cd32_se[] = { DIK_A, -1, DIK_D, -1, DIK_W, -1, DIK_S, -1, -1, DIK_LMENU, -1, DIK_LSHIFT, -1, DIK_NUMPAD7, -1, DIK_NUMPAD9, -1, DIK_DIVIDE, -1, DIK_SUBTRACT, -1, DIK_MULTIPLY, -1, -1 };

static int kb_cdtv[] = { DIK_NUMPAD1, -1, DIK_NUMPAD3, -1, DIK_NUMPAD7, -1, DIK_NUMPAD9, -1, -1 };

// static int kb_xa1[] = { DIK_NUMPAD4, -1, DIK_NUMPAD6, -1, DIK_NUMPAD8, -1, DIK_NUMPAD2, DIK_NUMPAD5, -1, DIK_LCONTROL, -1, DIK_LMENU, -1, DIK_SPACE, -1, -1 };
// static int kb_xa2[] = { DIK_D, -1, DIK_G, -1, DIK_R, -1, DIK_F, -1, DIK_A, -1, DIK_S, -1, DIK_Q, -1 };
// static int kb_arcadia[] = { DIK_F2, -1, DIK_1, -1, DIK_2, -1, DIK_5, -1, DIK_6, -1, -1 };
// static int kb_arcadiaxa[] = { DIK_1, -1, DIK_2, -1, DIK_3, -1, DIK_4, -1, DIK_6, -1, DIK_LBRACKET, DIK_LSHIFT, -1, DIK_RBRACKET, -1, DIK_C, -1, DIK_5, -1, DIK_Z, -1, DIK_X, -1, -1 };

static int* kbmaps[] = {
    kb_np, kb_ck, kb_se, kb_np3, kb_ck3, kb_se3,
    kb_cd32_np, kb_cd32_ck, kb_cd32_se,
    /*kb_xa1, kb_xa2, kb_arcadia, kb_arcadiaxa,*/ kb_cdtv
};

extern int ispressed(int key);

static int specialkeycode()
{
    return g_curr_conf.win32_specialkey;
}
static int specialpressed()
{
    return ispressed(specialkeycode());
}

static int shiftpressed()
{
    return ispressed(DIK_LSHIFT) || ispressed(DIK_RSHIFT);
}

static int altpressed()
{
    return ispressed(DIK_LMENU) || ispressed(DIK_RMENU);
}

static int ctrlpressed()
{
    return ispressed(DIK_LCONTROL) || ispressed(DIK_RCONTROL);
}

static int capslockstate;
static int host_capslockstate, host_numlockstate, host_scrolllockstate;

int getcapslockstate()
{
    return capslockstate;
}
void setcapslockstate(int state)
{
    capslockstate = state;
}

int getcapslock()
{
    int capstable[7];

    //  this returns bogus state if caps change when in exclusive mode..
    host_capslockstate = GetKeyState(VK_CAPITAL) & 1;
    host_numlockstate = GetKeyState(VK_NUMLOCK) & 1;
    host_scrolllockstate = GetKeyState(VK_SCROLL) & 1;
    capstable[0] = DIK_CAPITAL;
    capstable[1] = host_capslockstate;
    capstable[2] = DIK_NUMLOCK;
    capstable[3] = host_numlockstate;
    capstable[4] = DIK_SCROLL;
    capstable[5] = host_scrolllockstate;
    capstable[6] = 0;
    capslockstate = inputdevice_synccapslock(capslockstate, capstable);
    return capslockstate;
}

void clearallkeys()
{
    inputdevice_updateconfig(&g_curr_conf);
}

static int np[] = {
    DIK_NUMPAD0, 0, DIK_NUMPADPERIOD, 0, DIK_NUMPAD1, 1, DIK_NUMPAD2, 2,
    DIK_NUMPAD3, 3, DIK_NUMPAD4, 4, DIK_NUMPAD5, 5, DIK_NUMPAD6, 6, DIK_NUMPAD7, 7,
    DIK_NUMPAD8, 8, DIK_NUMPAD9, 9, -1
};

void my_kbd_handler(int keyboard, int scancode, int newstate)
{
    int code = 0;
    int scancode_new;
    static int swapperdrive = 0;

    if (scancode == specialkeycode())
        return;

    if (scancode == DIK_F11 && g_curr_conf.win32_ctrl_F11_is_quit && ctrlpressed())
        code = AKS_QUIT;

    scancode_new = scancode;
    if (!specialpressed() && inputdevice_iskeymapped(keyboard, scancode))
        scancode = 0;
    //  GUI must be always available
    if (scancode_new == DIK_F12 && g_curr_conf.win32_guikey < 0)
        scancode = scancode_new;
    if (scancode_new == g_curr_conf.win32_guikey && scancode_new != DIK_F12)
        scancode = scancode_new;

    // Logger::Write (L"keyboard = %d scancode = 0x%02x state = %d\n", keyboard, scancode, newstate );

    if (newstate == 0 && code == 0)
    {
        switch (scancode)
        {
            case DIK_SYSRQ:
                screenshot(specialpressed() ? 1 : 0, 1);
                break;
        }
    }

    if (newstate && code == 0)
    {
        if (scancode == DIK_F12 || scancode == g_curr_conf.win32_guikey)
        {
            if (ctrlpressed())
            {
                code = AKS_TOGGLEDEFAULTSCREEN;
            }
            else if (shiftpressed() || specialpressed())
            {
                if (isfullscreen() <= 0)
                {
                    disablecapture();
                    code = AKS_ENTERDEBUGGER;
                }
            }
            else
            {
                code = AKS_ENTERGUI;
            }
        }

        switch (scancode)
        {
            case DIK_F1:
            case DIK_F2:
            case DIK_F3:
            case DIK_F4:
                if (specialpressed())
                {
                    if (ctrlpressed())
                    {
                    }
                    else
                    {
                        if (shiftpressed())
                            code = AKS_EFLOPPY0 + (scancode - DIK_F1);
                        else
                            code = AKS_FLOPPY0 + (scancode - DIK_F1);
                    }
                }
                break;
            case DIK_F5:
                #if 0
                {
                    disk_prevnext(0, -1);
                    return;
                    // crap++;
                    // Logger::Write (L"%d\n", crap);
                }
                #endif
                if (specialpressed())
                {
                    if (shiftpressed())
                        code = AKS_STATESAVEDIALOG;
                    else
                        code = AKS_STATERESTOREDIALOG;
                }
                break;
            case DIK_1:
            case DIK_2:
            case DIK_3:
            case DIK_4:
            case DIK_5:
            case DIK_6:
            case DIK_7:
            case DIK_8:
            case DIK_9:
            case DIK_0:
                if (specialpressed())
                {
                    int num = scancode - DIK_1;
                    if (shiftpressed())
                        num += 10;
                    if (ctrlpressed())
                    {
                        swapperdrive = num;
                        if (swapperdrive > 3)
                            swapperdrive = 0;
                    }
                    else
                    {
                        int i;
                        for (i = 0; i < 4; i++)
                        {
                            if (!_tcscmp(g_curr_conf.floppies[i].df, g_curr_conf.dfxlist[num]))
                                g_changed_conf.floppies[i].df = "";
                        }
                        g_changed_conf.floppies[swapperdrive].df = g_curr_conf.dfxlist[num];
                        config_changed = 1;
                    }
                }
                break;
            case DIK_NUMPAD0:
            case DIK_NUMPAD1:
            case DIK_NUMPAD2:
            case DIK_NUMPAD3:
            case DIK_NUMPAD4:
            case DIK_NUMPAD5:
            case DIK_NUMPAD6:
            case DIK_NUMPAD7:
            case DIK_NUMPAD8:
            case DIK_NUMPAD9:
            case DIK_NUMPADPERIOD:
                if (specialpressed())
                {
                    int i = 0, v = -1;
                    while (np[i] >= 0)
                    {
                        v = np[i + 1];
                        if (np[i] == scancode)
                            break;
                        i += 2;
                    }
                    if (v >= 0)
                        code = AKS_STATESAVEQUICK + v * 2 + ((shiftpressed() || ctrlpressed()) ? 0 : 1);
                }
                break;
            case DIK_PAUSE:
                if (specialpressed())
                {
                    if (shiftpressed())
                        code = AKS_IRQ7;
                    else
                        code = AKS_WARP;
                }
                else
                {
                    code = AKS_PAUSE;
                }
                break;
            case DIK_SCROLL:
                code = AKS_INHIBITSCREEN;
                break;
            case DIK_NUMPADMINUS:
                if (specialpressed())
                {
                    if (shiftpressed())
                        code = AKS_DECREASEREFRESHRATE;
                    else if (ctrlpressed())
                        code = AKS_MVOLDOWN;
                    else
                        code = AKS_VOLDOWN;
                }
                break;
            case DIK_NUMPADPLUS:
                if (specialpressed())
                {
                    if (shiftpressed())
                        code = AKS_INCREASEREFRESHRATE;
                    else if (ctrlpressed())
                        code = AKS_MVOLUP;
                    else
                        code = AKS_VOLUP;
                }
                break;
            case DIK_NUMPADSTAR:
                if (specialpressed())
                {
                    if (ctrlpressed())
                        code = AKS_MVOLMUTE;
                    else
                        code = AKS_VOLMUTE;
                }
                break;
            case DIK_NUMPADSLASH:
                if (specialpressed())
                    code = AKS_STATEREWIND;
                break;
        }
    }

    if (code)
    {
        inputdevice_add_inputcode(code, 1);
        return;
    }

    scancode = scancode_new;
    if (!specialpressed() && newstate)
    {
        if (scancode == DIK_CAPITAL)
        {
            host_capslockstate = host_capslockstate ? 0 : 1;
            capslockstate = host_capslockstate;
        }
        if (scancode == DIK_NUMLOCK)
        {
            host_numlockstate = host_numlockstate ? 0 : 1;
            capslockstate = host_numlockstate;
        }
        if (scancode == DIK_SCROLL)
        {
            host_scrolllockstate = host_scrolllockstate ? 0 : 1;
            capslockstate = host_scrolllockstate;
        }
    }
    if (specialpressed())
        return;

    inputdevice_translatekeycode(keyboard, scancode, newstate);
}

void keyboard_settrans()
{
    inputdevice_setkeytranslation(keytrans, kbmaps);
}

int target_checkcapslock(int scancode, int* state)
{
    if (scancode != DIK_CAPITAL && scancode != DIK_NUMLOCK && scancode != DIK_SCROLL)
        return 0;
    if (*state == 0)
        return -1;
    if (scancode == DIK_CAPITAL)
        *state = host_capslockstate;
    if (scancode == DIK_NUMLOCK)
        *state = host_numlockstate;
    if (scancode == DIK_SCROLL)
        *state = host_scrolllockstate;
    return 1;
}